package local_driver

import (
	"context"
	"errors"
	"github.com/chicken-team-outside/chicken_transmission/driver"
	"github.com/chicken-team-outside/chicken_transmission/model"
	"os"
	"path/filepath"
	"sync"
)

type LocalDriver struct {
	basePath   string
	ctx        context.Context
	cancel     context.CancelFunc
	uniqueLock sync.Mutex
}

func (l *LocalDriver) Stat(name string) (os.FileInfo, error) {
	return os.Stat(l.getRealPath(name))
}

func (l *LocalDriver) checkParentOrCreate(path string) error {
	l.uniqueLock.Lock()
	defer l.uniqueLock.Unlock()
	parent := filepath.Dir(path)
	_, err := os.Stat(parent)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			err = os.MkdirAll(parent, os.ModeDir)
			if errors.Is(err, os.ErrExist) {
				err = nil
			}
		}
	}
	return err
}

func (l *LocalDriver) OpenFile(path string, flag int, mode os.FileMode) (driver.LocalDriverFile, error) {
	path = l.getRealPath(path)
	err := l.checkParentOrCreate(path)
	if err != nil {
		return nil, err
	}
	return os.OpenFile(path, flag, mode)
}

func NewLocalDriver(basePath string) *LocalDriver {
	d := &LocalDriver{basePath: basePath}
	d.ctx, d.cancel = context.WithCancel(context.Background())
	return d
}

func (l *LocalDriver) getRealPath(name string) string {
	if l.basePath == "" {
		return name
	}
	return filepath.Join(l.basePath, name)
}

func (l *LocalDriver) MkdirAll(path string, perm os.FileMode) error {
	if l.ctx.Err() != nil {
		return l.ctx.Err()
	}
	return os.MkdirAll(l.getRealPath(path), perm)
}

func (l *LocalDriver) Remove(path string) error {
	if l.ctx.Err() != nil {
		return l.ctx.Err()
	}
	return os.RemoveAll(l.getRealPath(path))
}

func (l *LocalDriver) ListFiles(dirPath string) ([]os.FileInfo, error) {
	if l.ctx.Err() != nil {
		return nil, l.ctx.Err()
	}
	d, err := os.ReadDir(l.getRealPath(dirPath))
	if err != nil {
		return nil, err
	}
	result := make([]os.FileInfo, len(d))
	for i, v := range d {
		if l.ctx.Err() != nil {
			return nil, l.ctx.Err()
		}
		result[i], err = v.Info()
		if err != nil {
			return nil, err
		}
	}
	return result, nil
}

func (l *LocalDriver) Truncate(path string, size int64) error {
	return os.Truncate(l.getRealPath(path), size)
}

func (l *LocalDriver) Rename(oldPath string, newPath string) error {
	if l.ctx.Err() != nil {
		return l.ctx.Err()
	}
	oldPath = l.getRealPath(oldPath)
	newPath = l.getRealPath(newPath)
	return os.Rename(oldPath, newPath)
}

func (l *LocalDriver) GetVendor() model.Vendor {
	return model.Vendor_Local
}

func (l *LocalDriver) Close() error {
	l.cancel()
	return nil
}
